/*
Kakine
ActionScript Framework
Copyright © 2011, PUNY, LLC.

This file is part of KAKINE.

KAKINE is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

KAKINE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with KAKINE.  If not, see <http://www.gnu.org/licenses/>.
*/ 
package com.puny.mvc.controllers.particle
{
	import com.puny.mvc.controllers.Controller;
	import com.puny.mvc.models.Model;
	import com.puny.mvc.views.singleviews.ParticleView;
	
	import flash.geom.Point;
	
	/**
	 * $Revision: 292 $<br/>
	 * Particle Controller
	 */
	public class ParticleLoopController extends Controller
	{
		private var _dt:Number;
		private var _dt2:Number;
		private var _models:Array = new Array();
		private var _pool:Array = new Array();
		private var _garbage:Array = new Array();
		private var _view:ParticleView;
		private var _world:IWorld;

		public function ParticleLoopController(world:IWorld) {
			_world = world;
			super();
		}
		
		/**
		 * @private
		 */
		override protected function setup():void {
			_view = _world.createView(_world.total);
			addChild(_view);
		}
		
		/**
		 * @private
		 */
		override public function beforeReady():void {
			if (_view.parent == null) {
				addChild(_view);
			}
			generateModels();
			dt = 1 / stage.frameRate;

			var model:Model;
			var i:int = 0;
			for each(model in _models) {
				model.view = _view.getMoveClip(i);
				initialize(model);
				update(model);
				_view.addChild(model.view);
				i++;
			}
			for each(model in _pool) {
				model.view = _view.getMoveClip(i);
				initialize(model);
				update(model);
				i++;
			}
		}

		/**
		 * Reset particles
		 */
		public function reset():void {
			addChild(_view);
			generateModels();			
		}
		
		/**
		 * @private
		 */
		override public function procedureOpen():Boolean {
			mainProcedure(initialize);
			pooling();
			return _pool.length > 0;
		}
		
		/**
		 * @private
		 */
		override public function procedureMainFrame():Boolean {
			mainProcedure(initialize);
			return true;
		}
		
		/**
		 * @private
		 */
		override public function procedureClose():Boolean {
			mainProcedure(closing);
			return _models.length > 0;
		}
		
		override public function afterClose():void {
			super.afterClose();
			removeChild(_view);
//			_world = null;
//			_view = null;
//			_models = null;
//			_pool = null;
		}
		
		/**
		 * @private
		 */
		protected function mainProcedure(fnc:Function):void {
			var model:Model;
			for each(model in _models) {
				model.acc = _world.gravity;
				model.acc = model.acc.add(_world.sumUpAccelaration(model));
				
				if (_world.isOver(model,stage)) {
					fnc(model);
				}
				update(model);
				
			}
		}
		
		private function set dt(value:Number):void {
			_dt = value;
			_dt2 = _dt * _dt;
		}
		
		private function generateModels():void {
			var model:Model;
			trace("Garbage size:", _garbage.length);
			for(var i:int = 0;i < _world.total;i++) {
				if (_garbage.length > 0) {
					model = _garbage.shift();
				} else {
					model = new Model();
				}
				model.x = 0;
				model.y = 0;
				model.acc = new Point(0,0);
				model.verletPos = [new Point(0,0),new Point(0,0)];
				if (i < _world.init) {
					_models.push(model);
				} else {
					_pool.push(model);
				}
			}
		}
		
		private function initialize(model:Model):void {
			model.acc = _world.gravity;
			model.acc = model.acc.add(_world.specialInitialize(model));
			model.verletPos[0].x = model.x;
			model.verletPos[0].y = model.y;
			model.verletPos[1].x = model.x;
			model.verletPos[1].y = model.y;
		}
		
		private function closing(model:Model):void {
			var index:int = _models.indexOf(model);
			_view.removeChild(model.view);
//			model.view = null;
			_garbage.push(model);
			_models.splice(index,1);
		}
		
		private function verlet(r0:Number,r1:Number,a:Number):Number {
			return 2 * r1 - r0 + a * _dt2;
		} 

		private function update(model:Model):void {
			model.x = verlet(model.verletPos[0].x, model.verletPos[1].x, model.acc.x);
			model.y = verlet(model.verletPos[0].y, model.verletPos[1].y, model.acc.y);
			var pt:Point = model.verletPos.shift();
			pt.x = model.x;
			pt.y = model.y;
			model.verletPos.push(pt);
			if (model.view != null) {
				model.view.x = pt.x;
				model.view.y = pt.y;
			}
		}

		private function pooling():void {
			var count:uint = _world.increment;
			while (count > 0 && _pool.length > 0) {
				var model:Model = _pool.shift();
				if (model.view != null) {
					_view.addChild(model.view);
				}
				_models.push(model);
				count--;
			}
		}
		
	}
}